package com.tjakus.arduino.hibernate;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import com.tjakus.arduino.logging.Logging;


public class HibernateUtil {

	/**
	 * This is a static class which represents database initialization and queries
	 * towards database.
	 * 
	 * @author tjakus
	 * 
	 */

	private static SessionFactory sessionFactory;

	/**
	 * This constructor initialize and crates connection towards MySQL database
	 * from hibernate.cfg.xml configuration file.
	 * 
	 */
	static {
		try {
			// Create the SessionFactory from hibernate.cfg.xml
			Logging.getLogger().info(
					"HibernateUtil - initialize sessionFactory - [ ]");
			Configuration configuration = new Configuration();
			configuration.configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(configuration.getProperties()).buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		} catch (Throwable e) {
			// Make sure you log the exception, as it might be swallowed
			Logging.getLogger().error(
					"HibernateUtil - buildSessionFactory - "
							+ "[ UNABLE TO BUILD SESSION ]", e);
		}
	}

	/**
	 * This method initialize and crates connection towards MySQL database from
	 * hibernate.cfg.xml configuration file.
	 * 
	 */
	public static boolean buildSessionFactory() {
		try {
			// Create the SessionFactory from hibernate.cfg.xml
			Logging.getLogger().info("HibernateUtil - buildSessionFactory - [ ]");
			Configuration configuration = new Configuration();
			configuration.configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(configuration.getProperties()).buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
			return true;
		} catch (Throwable e) {
			// Make sure you log the exception, as it might be swallowed
			Logging.getLogger().error(
					"HibernateUtil - buildSessionFactory - "
							+ "[ UNABLE TO BUILD SESSION ]", e);
			return false;
		}
	}

	/**
	 * This method returns Hibernate session factory.
	 * 
	 */
	public static SessionFactory getSessionFactory() {
		// Returns Hibernate session factory
		return sessionFactory;
	}

	/**
	 * This method inserts statistic data into database. It returns true or false.
	 * 
	 */
	public static boolean addStatistic(Statistics statistic) {
		Logging.getLogger().info(
				"HibernateUtil - addStatistic - [" + statistic.getSensorId() + " "
						+ statistic.getSensorValue() + " ]");
		Transaction trns = null;
		// Open session for Hibernate to connect to DB
		Session session = HibernateUtil.getSessionFactory().openSession();
		try {
			trns = session.beginTransaction();
			session.save(statistic);
			trns.commit();
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - addStatistic - [ UNABLE TO ADD ]", e);
			return false;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
		return true;
	}

	/**
	 * This method retrieves list of statistics from database by sensorId. It
	 * returns list of statistics.
	 * 
	 */
	@SuppressWarnings("unchecked")
	public static List<Statistics> getStatisticsBySensorId(String sensorId) {
		Transaction trns = null;
		List<Statistics> statisticsList = new ArrayList<Statistics>();
		Session session = HibernateUtil.getSessionFactory().openSession();
		Logging.getLogger().info(
				"HibernateUtil - getStatisticsBySensorId - [ " + sensorId + " ]");
		try {
			trns = session.beginTransaction();
			statisticsList = session
					.createQuery("from Statistics where sensorId =:sensorId")
					.setString("sensorId", sensorId).list();
			trns.commit();
			for (Iterator<Statistics> iter = statisticsList.iterator(); iter
					.hasNext();) {
				Statistics statistic = iter.next();
				Logging.getLogger().debug(
						"HibernateUtil - getStatisticsBySensorId - [ " + statistic.getId()
								+ " " + statistic.getSensorId() + " ]");
			}
			return statisticsList;
		} catch (RuntimeException e) {
			Logging.getLogger()
					.error(
							"HibernateUtil - getStatisticsBySensorId - "
									+ "[ UNABLE TO GET ]", e);
			return null;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
	}

	/**
	 * This method retrieves latest statistic from database by sensorId order by
	 * time stamp. It returns the latest statistics.
	 * 
	 */
	public static Statistics getLatestStatisticsBySensorId(String sensorId) {
		Transaction trns = null;
		Statistics statistic;
		Session session = HibernateUtil.getSessionFactory().openSession();
		Logging.getLogger().info(
				"HibernateUtil - getLatestStatisticsBySensorId - [ " + sensorId + " ]");
		try {
			trns = session.beginTransaction();
			statistic = (Statistics) session
					.createQuery(
							"SELECT statistic from Statistics statistic where sensorId=:sensorId ORDER BY statistic.timestamp DESC")
					.setString("sensorId", sensorId).setMaxResults(1).uniqueResult();
			trns.commit();
			Logging.getLogger().debug(
					"HibernateUtil - getLatestStatisticsBySensorId - [ "
							+ statistic.getSensorId() + " " + statistic.getSensorValue()
							+ " ]");
			return statistic;
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - getLatestStatisticsBySensorId - "
							+ "[ UNABLE TO GET ]", e);
			return null;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public static List<Sensors> getAllSensors() {
		Transaction trns = null;
		List<Sensors> sensorsList = new ArrayList<Sensors>();
		Session session = HibernateUtil.getSessionFactory().openSession();
		Logging.getLogger().info(
				"HibernateUtil - getAllSensors - [ ]");
		try {
			trns = session.beginTransaction();
			sensorsList = session.createQuery("from Sensors").list();
			trns.commit();
			for (Iterator<Sensors> iter = sensorsList.iterator(); iter
					.hasNext();) {
				Logging.getLogger().debug(
						"HibernateUtil - getAllSensors - [" + iter.next().getValue()	+ " ]");
			}
			return sensorsList;
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - getAllSensors - "
							+ "[ UNABLE TO GET ]", e);
			return null;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
	}
	
	/**
	 * This method updates sensor in database. It returns true or false.
	 * 
	 */
	public static boolean updateSensor(Sensors sensor) {
		Transaction trns = null;
		Logging.getLogger().info(
				"HibernateUtil - updateSensor - [" + sensor.getSensorId() + " - " +
						 sensor.getValue() + " ]");
		Session session = HibernateUtil.getSessionFactory().openSession();
		try {
			trns = session.beginTransaction();
			session.update(sensor);
			trns.commit();
			return true;
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - updateSensor - " + "[ UNABLE TO UPDATE ]", e);
			return false;
		} finally {
			session.flush();
			session.close();
		}
	}
	
	/**
	 * This method retrieves sensor from database by sensorId
	 * 
	 */
	public static Sensors getSensorBySensorId(String sensorId) {
		Transaction trns = null;
		Sensors sensor;
		Session session = HibernateUtil.getSessionFactory().openSession();
		Logging.getLogger().info(
				"HibernateUtil - getSensorBySensorId - [ " + sensorId + " ]");
		try {
			trns = session.beginTransaction();
			sensor = (Sensors) session
					.createQuery(
							"from Sensors where sensorId=:sensorId")
					.setString("sensorId", sensorId).uniqueResult();
			trns.commit();
			Logging.getLogger().debug(
					"HibernateUtil - getSensorBySensorId - [ "
							+ sensor.getSensorId() + " " + sensor.getValue()
							+ " ]");
			return sensor;
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - getSensorBySensorId - "
							+ "[ UNABLE TO GET ]", e);
			return null;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
	}
	
	/**
	 * This method retrieves sensors from database by changeable
	 * 
	 */
	@SuppressWarnings("unchecked")
	public static List<Sensors> getSensorByChangeable(boolean changeable) {
		Transaction trns = null;
		List<Sensors> sensorsList = new ArrayList<Sensors>();
		Session session = HibernateUtil.getSessionFactory().openSession();
		Logging.getLogger().info(
				"HibernateUtil - getSensorByChangeable - [ " + changeable + " ]");
		try {
			trns = session.beginTransaction();
			sensorsList = session
					.createQuery(
							"from Sensors where changeable=:changeable")
					.setBoolean("changeable", changeable).list();
			trns.commit();
			for (Iterator<Sensors> iter = sensorsList.iterator(); iter
					.hasNext();) {
				Logging.getLogger().debug(
						"HibernateUtil - getSensorByChangeable - [" + iter.next().getSensorId()	+ " ]");
			}
			return sensorsList;
		} catch (RuntimeException e) {
			Logging.getLogger().error(
					"HibernateUtil - getSensorByChangeable - "
							+ "[ UNABLE TO GET ]", e);
			return null;
		} finally {
			// Flush and close the session
			session.flush();
			session.close();
		}
	}
	
}